<?php

namespace app\service\modbus;

use EchoLogger;
use Exception;
use ModbusTcpClient\Composer\Read\ReadRegistersBuilder;
use ModbusTcpClient\Composer\Write\WriteRegistersBuilder;
use ModbusTcpClient\Network\BinaryStreamConnection;
use ModbusTcpClient\Network\NonBlockingClient;
use ModbusTcpClient\Packet\ModbusFunction\ReadHoldingRegistersRequest;
use ModbusTcpClient\Packet\ModbusFunction\ReadHoldingRegistersResponse;
use ModbusTcpClient\Packet\ModbusFunction\WriteMultipleRegistersRequest;
use ModbusTcpClient\Packet\ModbusFunction\WriteSingleRegisterRequest;
use ModbusTcpClient\Packet\ResponseFactory;
use ModbusTcpClient\Packet\RtuConverter;
use ModbusTcpClient\Utils\Endian;
use ModbusTcpClient\Utils\Packet;
use ModbusTcpClient\Utils\Types;
use support\Log;
use support\Response;

class ModbusTcpService
{
    protected $connection;
    protected $host;
    protected $port;

    public function __construct($host = '127.0.0.1', $port = 502)
    {
        $this->connection = BinaryStreamConnection::getBuilder()
            ->setPort($port)
            ->setHost($host)
            ->setConnectTimeoutSec(1.5) // timeout when establishing connection to the server
            ->setWriteTimeoutSec(0.5) // timeout when writing/sending packet to the server
            ->setReadTimeoutSec(0.3) // timeout when waiting response from server
            ->build();
        $this->host = $host;
        $this->port = $port;
    }

    /**
     * 判断modbus寄存器是否在线
     * @return string
     */
    public function isOnline(): string
    {
        $connection = $this->connection;
        try {
            $connection->connect();
            $is_online = "online";
        } catch (\Exception $exception) {
            $is_online = "offline";
        } finally {
            $connection->close();
        }
        return $is_online;
    }

    /**
     * 获取modbus-tcp的值
     * @param int $quantity 数量
     * @param int $unitID 站号
     * @param int $start_point 起始地址
     * @param int $end_point 结束地址
     * @return array|int
     */
    public function getModbusTcpValue(int $unitID = 1, int $start_point = 0, int $end_point = 10, int $quantity = 1)
    {
        Endian::$defaultEndian = Endian::BIG_ENDIAN_LOW_WORD_FIRST; // set default (global) endian used for parsing data
        $connection = $this->connection;
        $data = [];
        if ($start_point == $end_point) {
            $packet = new ReadHoldingRegistersRequest($start_point, $quantity, $unitID); // NB: This is Modbus TCP packet not Modbus RTU over TCP!
            try {
                $binaryData = $connection->connect()->sendAndReceive($packet);
                /**
                 * @var $response ReadHoldingRegistersResponse
                 */
                $response = ResponseFactory::parseResponseOrThrow($binaryData);
                $value = 0;
                foreach ($response as $word) {
                    $value = $word->getBytes()[1];
                }
                return $value;
            } catch (Exception $exception) {
                Log::error('An exception occurred' . $exception->getMessage());
                $data['error'] = 'An exception occurred' . $exception->getMessage() . $exception->getTraceAsString();
            } finally {
                $connection->close();
            }
        } else {
            for ($i = $start_point; $i <= $end_point; $i++) {
                $startAddress = $i;
                $packet = new ReadHoldingRegistersRequest($startAddress, $quantity, $unitID); // NB: This is Modbus TCP packet not Modbus RTU over TCP!
                try {
                    $binaryData = $connection->connect()->sendAndReceive($packet);
                    /**
                     * @var $response ReadHoldingRegistersResponse
                     */
                    $response = ResponseFactory::parseResponseOrThrow($binaryData);
                    foreach ($response as $word) {
                        $data[] = [
                            'point' => $i,
                            'value' => $word->getBytes()[1]
                        ];
                    }
                } catch (Exception $exception) {
                    Log::error('An exception occurred' . $exception->getMessage());
                    $data['error'] = 'An exception occurred' . $exception->getMessage() . $exception->getTraceAsString();
                } finally {
                    $connection->close();
                }
            }
        }

        return $data;
    }

    /**
     * 设置modbus-tcp的值
     * @param int $point 地址
     * @param int $value 值
     * @param int $unitId 站号
     * @return array|Response
     */
    public function setModbusTcpValue(int $point = 0, int $value = 0, int $unitId = 1)
    {
        $connection = $this->connection;
        $data_value = modbus_data_type($value);
        if (empty($data_value['value'])) {
            return errorMessage("数据类型错误");
        }
        if ($data_value['error']) {
            return errorMessage($data_value['error']);
        }
        $v = $data_value['value'];
        $binary_data = "";
        try {
            $packet1 = new WriteMultipleRegistersRequest($point, [$v], $unitId);
            $binaryData = $connection->connect()->sendAndReceive($packet1);
            //unpack('H*', $binaryData)[1])
            $binary_data = unpack('H*', $binaryData)[1];
        } catch (Exception $exception) {
            Log::error('An exception occurred' . $exception->getMessage());
            return errorMessage('An exception occurred' . $exception->getMessage());
        } finally {
            $connection->close();
        }
        return $binary_data;
    }

    /**
     * 获取modbus-tcp的值,根据数据类型进行获取
     * @param int $unitID 站号
     * @param array $data_array 数据集合
     * @return string |array
     */
    public function getModbusTcpTypeRead(array $data_array, int $unitID = 1)
    {

        if (empty($data_array)) {
            return errorMessage("数据有误");
        }
        Endian::$defaultEndian = Endian::BIG_ENDIAN_LOW_WORD_FIRST; // set default (global) endian used for parsing data
        $data = [];
        $keyToCheck = "start_address";
        $keyType = "get_type";
        $keyName = "name";
        if (!array_key_exists($keyToCheck, $data_array[0])) {
            return errorMessage("数据中不存在键名【start_address】");
        }
        if (!array_key_exists($keyName, $data_array[0])) {
            return errorMessage("数据中不存在键名【name】");
        }
        if (!array_key_exists($keyType, $data_array[0])) {
            return errorMessage("数据中不存在键名【get_type】");
        }
        $uri = "tcp://{$this->host}:{$this->port}";
        $server = ReadRegistersBuilder::newReadHoldingRegisters($uri,$unitID);
        foreach ($data_array as $value) {
            $start_address = $value['start_address'];
            $get_type = $value['get_type'];
            $name = $value['name'];
            switch ($get_type) {
                case "bit":
                    if (!array_key_exists('bit', $value)) {
                        return errorMessage("数据中不存在键名【bit】");
                    }
                    $bit = $value['bit'];
                    $server = $server->bit($start_address, $bit, $name);
                    break;
                case "int16":
                    $server = $server->int16($start_address, $name);
                    break;
                case "uint16":
                    $server = $server->uint16($start_address, $name);
                    break;
                case "int32":
                    $server = $server->int32($start_address, $name);
                    break;
                case "uint32":
                    $server = $server->uint32($start_address, $name);
                    break;
                case "uint64":
                    $server = $server->uint64($start_address, $name);
                    break;
                case "float":
                    $server = $server->float($start_address, $name);
                    break;
                case "string":
                    $server = $server->string($start_address, $name);
                    break;
                default:
                    $server = $server->byte($start_address, $name);
                    break;
            }
        }
        $requests = $server->build();
        try {
            foreach ($requests as $request) {
                $client = new NonBlockingClient(['readTimeoutSec' => 0.5, NonBlockingClient::OPT_FLAT_REQUEST_RESPONSE => true]);
                $response = $client->sendRequest($request);
                $data = $response->getData();
            }
            return $data;
        } catch (\Exception $exception) {
            return $exception->getMessage();
        }
    }

    /**
     * 写入modbus-tcp的值,根据数据类型进行写入
     * @param int $unitID 站号
     * @param array $data_array 数据集合
     * @return string |array
     */
    public function getModbusTcpTypeWrite(array $data_array, int $unitID = 1)
    {

        if (empty($data_array)) {
            return errorMessage("数据有误");
        }
        Endian::$defaultEndian = Endian::BIG_ENDIAN_LOW_WORD_FIRST; // set default (global) endian used for parsing data
        $data = [];
        $keyToCheck = "start_address";
        $keyType = "get_type";
        $keyValue = "value";
        if (!array_key_exists($keyToCheck, $data_array[0])) {
            return errorMessage("数据中不存在键名【start_address】");
        }
        if (!array_key_exists($keyValue, $data_array[0])) {
            return errorMessage("数据中不存在键名【value】");
        }
        if (!array_key_exists($keyType, $data_array[0])) {
            return errorMessage("数据中不存在键名【get_type】");
        }
        $uri = "tcp://{$this->host}:{$this->port}";
        $server = WriteRegistersBuilder::newWriteMultipleRegisters($uri,$unitID);
        foreach ($data_array as $value) {
            $start_address = $value['start_address'];
            $get_type = $value['get_type'];
            $_value = $value['value'];
            switch ($get_type) {
                case "bit":
                case "byte":
                    return errorMessage("类型不存在【bit,byte】");
                case "int16":
                    $server = $server->int16($start_address,$_value);
                    break;
                case "uint16":
                    $server = $server->uint16($start_address,$_value);
                    break;
                case "int32":
                    $server = $server->int32($start_address, $_value);
                    break;
                case "uint32":
                    $server = $server->uint32($start_address, $_value);
                    break;
                case "uint64":
                    $server = $server->uint64($start_address, $_value);
                    break;
                case "int64":
                    $server = $server->int64($start_address, $_value);
                    break;
                case "float":
                    $server = $server->float($start_address, $_value);
                    break;
                case "double":
                    $server = $server->double($start_address, $_value);
                    break;
                default:
                    $server = $server->string($start_address, $_value);
                    break;
            }
        }
        $requests = $server->build();
        try {
            foreach ($requests as $request) {
                $client = new NonBlockingClient(['readTimeoutSec' => 0.5, NonBlockingClient::OPT_FLAT_REQUEST_RESPONSE => true]);
                $response = $client->sendRequest($request);
                $data = $response->getData();
            }
            return $data;
        } catch (\Exception $exception) {
            return $exception->getMessage();
        }
    }

    /**
     * 批量读取modbus-tcp的值,根据数据类型进行读取
     * @param array $data_array
     * @param int $unitID
     * @return array|string|Response
     */
    public function getModbusTcpTypeMultipleRead(array $data_array, int $unitID = 1)
    {
        if (empty($data_array)) {
            return errorMessage("数据有误");
        }
        Endian::$defaultEndian = Endian::BIG_ENDIAN_LOW_WORD_FIRST;
        $data = [];
        $keyToCheck = "start_address";
        $keyType = "get_type";
        $name = "name";
        $uri="uri";
        if (!array_key_exists($keyToCheck, $data_array[0])) {
            return errorMessage("数据中不存在键名【start_address】");
        }
        if (!array_key_exists($name, $data_array[0])) {
            return errorMessage("数据中不存在键名【name】");
        }
        if (!array_key_exists($uri, $data_array[0])) {
            return errorMessage("数据中不存在键名【uri】");
        }
        if (!array_key_exists($keyType, $data_array[0])) {
            return errorMessage("数据中不存在键名【get_type】");
        }
        foreach ($data_array as $value) {
            $start_address = $value['start_address'];
            $uri = $value['uri'];
            //字符串是否包含tcp
            if (strpos($uri, "tcp://")){
                var_dump(20);
                return errorMessage("uri格式错误");
            }
            $server = ReadRegistersBuilder::newReadHoldingRegisters($uri,$unitID);
            $get_type = $value['get_type'];
            $name = $value['name'];
            switch ($get_type) {
                case "bit":
                    if (!array_key_exists('bit', $value)) {
                        return errorMessage("数据中不存在键名【bit】");
                    }
                    $bit = $value['bit'];
                    $server = $server->bit($start_address, $bit, $name);
                    break;
                case "byte":
                    $byte = $value['byte'];
                    $server = $server->byte($start_address,$byte,$name);
                    break;
                case "int16":
                    $server = $server->int16($start_address, $name);
                    break;
                case "uint16":
                    $server = $server->uint16($start_address, $name);
                    break;
                case "int32":
                    $server = $server->int32($start_address, $name);
                    break;
                case "uint32":
                    $server = $server->uint32($start_address, $name);
                    break;
                case "uint64":
                    $server = $server->uint64($start_address, $name);
                    break;
                case "float":
                    $server = $server->float($start_address, $name);
                    break;
                case "string":
                    $server = $server->string($start_address, $name);
                    break;
                default:
                    $server = $server->byte($start_address, $name);
                    break;
            }
            $requests = $server->build();
            try {
                foreach ($requests as $request) {
                    $client = new NonBlockingClient(['readTimeoutSec' => 0.5, NonBlockingClient::OPT_FLAT_REQUEST_RESPONSE => true]);
                    $response = $client->sendRequest($request);
                    $getData = $response->getData();
                    foreach ($getData as $k=>$v){
                        $data[$k] = $v;
                    }
                    //$data[] = $response->getData();
                }
            } catch (\Exception $exception) {
                //$this->getModbusTcpTypeMultipleRead($data_array);
                return $exception->getMessage();
            }
        }
        return $data;
    }


    /**
     * tcp与rtu的读取
     * @param array $data_array
     * @param int $quantity
     * @param int $unitID RTU数据包从属id等效于Modbus TCP单元id
     * @return array|mixed[]|Response|string
     */
    public function getTcpRtu(array $data_array,int $quantity,int $unitID)
    {
        if (empty($data_array)) {
            return errorMessage("数据有误");
        }
        Endian::$defaultEndian = Endian::BIG_ENDIAN_LOW_WORD_FIRST; // set default (global) endian used for parsing data
        $data = [];
        $keyToCheck = "start_address";
        $keyType = "get_type";
        $keyName = "name";
        if (!array_key_exists($keyToCheck, $data_array[0])) {
            return errorMessage("数据中不存在键名【start_address】");
        }
        if (!array_key_exists($keyName, $data_array[0])) {
            return errorMessage("数据中不存在键名【name】");
        }
        if (!array_key_exists($keyType, $data_array[0])) {
            return errorMessage("数据中不存在键名【get_type】");
        }
        $connection = BinaryStreamConnection::getBuilder()
            ->setPort($this->port)
            ->setHost($this->host)
            ->setReadTimeoutSec(0.5)
            ->setIsCompleteCallback(function ($binaryData, $streamIndex) {
                return Packet::isCompleteLengthRTU($binaryData);
            })
            ->setLogger(new EchoLogger())
            ->build();

        $server =  ReadRegistersBuilder::newReadHoldingRegisters('no_address',$unitID);
        foreach ($data_array as $value) {
            $start_address = $value['start_address'];
            $get_type = $value['get_type'];
            $name = $value['name'];
            switch ($get_type) {
                case "bit":
                    if (!array_key_exists('bit', $value)) {
                        return errorMessage("数据中不存在键名【bit】");
                    }
                    $bit = $value['bit'];
                    $server = $server->bit($start_address, $bit, $name);
                    break;
                case "int16":
                    $server = $server->int16($start_address, $name);
                    break;
                case "uint16":
                    $server = $server->uint16($start_address, $name);
                    break;
                case "int32":
                    $server = $server->int32($start_address, $name);
                    break;
                case "uint32":
                    $server = $server->uint32($start_address, $name);
                    break;
                case "uint64":
                    $server = $server->uint64($start_address, $name);
                    break;
                case "float":
                    $server = $server->float($start_address, $name);
                    break;
                case "string":
                    $server = $server->string($start_address, $name);
                    break;
                default:
                    $server = $server->byte($start_address, $name);
                    break;
            }
        }
        $requests = $server->build();
        try {
            foreach ($requests as $request) {
                $rtuPacket = RtuConverter::toRtu($request->getRequest());
                $binaryData = $connection->connect()->sendAndReceive($rtuPacket);
                $tcpResponsePacket = RtuConverter::fromRtu($binaryData);
                $data[] = $request->parse($tcpResponsePacket);
            }
            return $data;
        } catch (\Exception $exception) {
            return $exception->getMessage();
        }
    }

    /**
     * 获取寄存器上的某一个值
     * @param int $address
     * @param string $name
     * @param string $get_type
     * @param int $bit
     * @param int $unitID
     * @return array|string|Response
     */
    public function getValue(int $address, string $name, string $get_type = 'int16', int $bit = -99, int $unitID = 1)
    {
        Endian::$defaultEndian = Endian::BIG_ENDIAN_LOW_WORD_FIRST;
        $ip = $this->host;
        $port = $this->port;
        $server = ReadRegistersBuilder::newReadHoldingRegisters('tcp://' . $ip . ":" . $port, $unitID);

        switch ($get_type) {
            case "bit":
                if ($bit < 0 || $bit > 16) {
                    return "bit位有误";
                }
                $server = $server->bit($address, $bit, $name);
                break;
            case "int16":
                $server = $server->int16($address, $name);
                break;
            case "uint16":
                $server = $server->uint16($address, $name);
                break;
            case "int32":
                $server = $server->int32($address, $name);
                break;
            case "uint32":
                $server = $server->uint32($address, $name);
                break;
            case "uint64":
                $server = $server->uint64($address, $name);
                break;
            case "float":
                $server = $server->float($address, $name);
                break;
            case "string":
                $server = $server->string($address, $name);
                break;
            default:
                $server = $server->byte($address, $name);
                break;
        }
        $requests = $server->build();
        $data = [];
        try {
            foreach ($requests as $request) {
                $client = new NonBlockingClient(['readTimeoutSec' => 0.5, NonBlockingClient::OPT_FLAT_REQUEST_RESPONSE => true]);
                $response = $client->sendRequest($request);
                $data = $response->getData();
            }
            return $data;
        } catch (\Exception $exception) {
            return $exception->getMessage();
        }
    }


    /**
     * 设置寄存器上的某一个值
     * @param int $address
     * @param int $value
     * @param string $get_type
     * @param int $bit
     * @param int $unitID
     * @return array|string|Response
     */
    public function setValue(int $address, int $value, string $get_type = 'int16', int $bit = -99, int $unitID = 1)
    {
        Endian::$defaultEndian = Endian::BIG_ENDIAN_LOW_WORD_FIRST;
        $uri = "tcp://{$this->host}:{$this->port}";
        $server = WriteRegistersBuilder::newWriteMultipleRegisters($uri, $unitID);
        switch ($get_type) {
            case "bit":
                if ($bit < 0 || $bit > 16) {
                    return errorMessage("bit位有误");
                }
                if (in_array($value,[0,1])){
                    return errorMessage("bit值只有0,1");
                }
                //bit转换成uint16
                $arr=[
                    //采样口选择 16bit 前8是高字节 后8是低字节
                    ['start_address'=>$address,'get_type'=>'uint16','name'=>'name'],
                ];
                $data = $this->getModbusTcpTypeRead($arr);
                $data_model = $data['name'];
                $string = decbin($data_model);
                $string = str_pad($string,16,"0",STR_PAD_LEFT);
                //目标的点位是在高位还是低位
                if ($bit == 0) {
                    $pos = -1;
                } else {
                    $pos = "-" . ($bit + 1);
                }
                $string = str_pad($string, 16, "0", STR_PAD_LEFT);
                $data_model_new_string = bindec(substr_replace($string, $value, $pos, 1));
                $arr=[
                    ['start_address'=>$address,'get_type'=>"uint16",'value'=>$data_model_new_string],
                ];
                $this->getModbusTcpTypeWrite($arr);
                return true;
            case "int16":
                $server = $server->int16($address, $value);
                break;
            case "uint16":
                $server = $server->uint16($address, $value);
                break;
            case "int32":
                $server = $server->int32($address, $value);
                break;
            case "uint32":
                $server = $server->uint32($address, $value);
                break;
            case "uint64":
                $server = $server->uint64($address, $value);
                break;
            case "float":
                $server = $server->float($address, $value);
                break;
            case "string":
                $server = $server->string($address, $value);
                break;
            default:
                return errorMessage("类型有误");
        }
        $server->build();
        return true;
    }

    /**
     * 获取开始值范围内的寄存器的值
     * @param int $startAddress
     * @param int $number
     * @param int $unitID
     * @return array|string
     */
    public function getAddressList(int $startAddress, int $number, int $unitID = 1): array|string
    {
        try {
            $uri = "tcp://{$this->host}:{$this->port}";
            $server = ReadRegistersBuilder::newReadHoldingRegisters($uri, $unitID);
            $get_data = [];
            for ($i = $startAddress; $i <= ($startAddress + $number); $i++) {
                $server = $server->uint16($i, $i);
                $get_data[] = $i;
            }
            $data = [];
            $requests = $server->build();

            foreach ($requests as $request) {
                $client = new NonBlockingClient(['readTimeoutSec' => 0.5, NonBlockingClient::OPT_FLAT_REQUEST_RESPONSE => true]);
                $response = $client->sendRequest($request);
                $get_data_all = $response->getData();
                if ($get_data_all) {
                    for ($j = 0; $j < $number; $j++) {
                        $data[] = [$get_data[$j] => $get_data_all[$j]];
                    }
                }
            }
            return $data;
        } catch (\Exception $exception) {
            return errorMessage($exception->getMessage());
        }
    }
    /**
     * 获取modbus-tcp写入参数的请求和响应报文
     * @param int $startAddress
     * @param int $value
     * @param string $dataType
     * @param int $unitID
     * @return array|string
     */
    public function getModbusWriteMessage(int $startAddress, int $value, string $dataType, int $unitID = 1): array|string
    {
        $endianess = Endian::BIG_ENDIAN_LOW_WORD_FIRST;
        $error = "";
        $v = "";
        switch ($dataType) {
            case 'uint8':
                if ($value < 0 || $value > 255) {
                    $error = "valid range of uint8 is 0 to 255";
                } else {
                    $v = Types::toByte($value);
                    if ($endianess & Endian::BIG_ENDIAN) {
                        $v = "\x00" . $v;
                    } else {
                        $v = $v . "\x00";
                    }
                }
                break;
            case 'int8':
                if ($value < -128 || $value > 127) {
                    $error = "valid range of int8 is -128 to 127";
                } else {
                    $v = Types::toByte($value);
                }
                if ($endianess & Endian::BIG_ENDIAN) {
                    $v = "\x00" . $v;
                } else {
                    $v = $v . "\x00";
                }
                break;
            case 'uint16':
                if ($value < 0 || $value > 65535) {
                    $error = "valid range of uint16 is 0 to 65535";
                } else {
                    $v = Types::toUint16($value);
                }
                break;
            case 'int16':
                if ($value < -32768 || $value > 32767) {
                    $error = "valid range of uint16 is -32768 to 32767";
                } else {
                    $v = Types::toInt16($value);
                }
                break;
            case 'uint32':
                if ($value < 0 || $value > 4294967295) {
                    $error = "valid range of uint32 is 0 to 4294967295";
                } else {
                    $v = Types::toUint32($value);
                }
                break;
            case 'int32':
                if ($value < -2147483648 || $value > 2147483647) {
                    $error = "valid range of int32 is -2147483648 to 2147483647";
                } else {
                    $v = Types::toInt32($value);
                }
                break;
            case 'float32':
                if ($value < -3.4e+38 || $value > 3.4e+38) {
                    $error = "valid range of float32 is -3.4e+38 to 3.4e+38";
                } else {
                    $v = Types::toReal($value);
                }
                break;
            case 'uint64':
                $value = (int)$value;
                if ($value < 0 || $value > 9223372036854775807) {
                    $error = "valid range of uint64 is 0 to 9223372036854775807";
                } else {
                    $v = Types::toUint64($value);
                }
                break;
            case 'int64':
                if ($value < 0 || $value > 9223372036854775807) {
                    $error = "valid range of int64 is -9223372036854775808 to 9223372036854775807";
                } else {
                    $v = Types::toInt64($value);
                }
                break;
            case 'float64':
                $value = (double)$value;
                if ($value < 1.7E-308 || $value > 1.7E+308) {
                    $error = "valid range of float64 is 1.7E-308 to 1.7E+308";
                } else {
                    $v = Types::toDouble($value);
                }
                break;
            default:
                $error = "invalid data type";
                break;
        }
        if ($error) {
            return errorMessage($error);
        }
        try {
            $packet = new WriteMultipleRegistersRequest($startAddress, [$v], $unitID);
            $binaryData = $this->connection->connect()->sendAndReceive($packet);
            $message['response_message'] = $packet->toHex();
            $message['request_message'] = unpack('H*', $binaryData)[1];
            return $message;
        } catch (\Exception $exception) {
            return errorMessage($exception->getMessage());
        }
    }
    /**
     * 获取modbus-tcp读取参数的请求和响应报文
     * @param int $startAddress 开始地址
     * @param string $dataType 数据类型
     * @param int $quantity 数量
     * @param int $unitID
     * @return array|string
     */
    public function getModbusReadMessage(int $startAddress, string $dataType = "uint16", int $quantity = 1, int $unitID = 1): array|string
    {
        if ($quantity < 1 || $quantity > 124) {
            return errorMessage("quantity值超出范围,只能在1-124之间");
        }
        try {
            $result = [];
            $packet = new ReadHoldingRegistersRequest($startAddress, $quantity, $unitID);
            $binaryData = $this->connection->connect()->sendAndReceive($packet);
            $result['response_message'] = $packet->toHex();
            $result['request_message'] = unpack('H*', $binaryData)[1];
            $response = ResponseFactory::parseResponseOrThrow($binaryData);
            $response = $response->withStartAddress($startAddress);
            foreach ($response as $address => $word) {
                $doubleWord = isset($response[$address + 1]) ? $response->getDoubleWordAt($address) : null;
                $quadWord = null;
                $value = match ($dataType) {
                    'uint16' => $word->getUInt16(),
                    'int16' => $word->getInt16(),
                    default => '-',
                };
                if (isset($response[$address + 3])) {
                    $quadWord = $response->getQuadWordAt($address);
                    $value = match ($dataType) {
                        'uint16' => $word->getUInt16(),
                        'int16' => $word->getInt16(),
                        'uint32' => $doubleWord->getUInt32(),
                        'int32' => $doubleWord->getInt32(),
                        'float32' => $doubleWord->getFloat(),
                        'uint64' => $quadWord->getUInt64(),
                        'int64' => $quadWord->getInt64(),
                        default => '-',
                    };
                }
                $highByteAsInt = $word->getHighByteAsInt();
                $lowByteAsInt = $word->getLowByteAsInt();
                $result[$address] = [
                    'highByte' => '0x' . str_pad(dechex($highByteAsInt), 2, '0', STR_PAD_LEFT),
                    'lowByte' => '0x' . str_pad(dechex($lowByteAsInt), 2, '0', STR_PAD_LEFT),
                    'highByteBits' => sprintf('%08d', decbin($highByteAsInt)),
                    'lowByteBits' => sprintf('%08d', decbin($lowByteAsInt)),
                    'value' => $value,
                ];
            }
            return $result;
        } catch (\Exception $exception) {
            return errorMessage($exception->getMessage());
        }
    }

    /**
     * modbus-tcp FC6数据读取 --Write Single Register(写入单个寄存器)
     * @param int $startAddress 地址
     * @param int $value 值
     * @param int $unitID
     * @return array|string
     */
    public function getWriteFC6(int $startAddress, int $value, int $unitID = 1): array|string
    {
        $packet = new WriteSingleRegisterRequest($startAddress, $value, $unitID);
        try {
            $binaryData = $this->connection->connect()->sendAndReceive($packet);
            $response = ResponseFactory::parseResponseOrThrow($binaryData);
            $result['response_message'] =$packet->toHex();
            $result['request_message'] = $response->toHex();
            $result['address'] = $startAddress;
            $result['value'] = $value;
            return $result;
        }catch (\Exception $exception){
            return errorMessage($exception->getMessage());
        }
    }

    /**
     * 请求的地址参数编程请求报文
     * @param int $address
     * @param int $value
     * @param int $unitID
     * @return array
     */
    public function setRequestMessage(int $address,int $value, int $unitID = 1): array
    {
        $packet = new WriteSingleRegisterRequest($address, $value, $unitID);
        return ['request_message'=>$packet->toHex()];
    }

    /**
     * 读取
     * @param int $address
     * @param int $bit
     * @param int $unitID
     * @return array|string
     */
    public function getBitReadValue(int $address=0,int $bit=0, int $unitID = 1): array|string
    {
        if ($bit < 0 || $bit > 15) {
            return errorMessage("bit位有误");
        }
        $uri = "tcp://{$this->host}:{$this->port}";
        try {
            $server = ReadRegistersBuilder::newReadHoldingRegisters($uri, $unitID);
            $requests = $server->bit($address, $bit,'value')->build();
            $client = new NonBlockingClient(['readTimeoutSec' => 0.5, NonBlockingClient::OPT_FLAT_REQUEST_RESPONSE => true]);
            $response = $client->sendRequests($requests);
            return $response->getData();
        }catch (\Exception $exception){
            return errorMessage($exception->getMessage());
        }
    }
    /**
     * 读取
     * @param int $address
     * @param bool $bit
     * @param int $unitID
     * @return array|string
     */
    public function getByteReadValue(int $address=0,bool $bit=true, int $unitID = 1): array|string
    {
        $uri = "tcp://{$this->host}:{$this->port}";
        try {
            $server = ReadRegistersBuilder::newReadHoldingRegisters($uri, $unitID);
            $requests = $server->byte($address, $bit,'value')->build();
            $client = new NonBlockingClient(['readTimeoutSec' => 0.5, NonBlockingClient::OPT_FLAT_REQUEST_RESPONSE => true]);
            $response = $client->sendRequests($requests);
            return $response->getData();
        }catch (\Exception $exception){
            return errorMessage($exception->getMessage());
        }
    }
    /**
     * 设置bit位的值
     * @param int $address
     * @param int $bit
     * @param int $value
     * @param int $unitID
     * @return array|string
     */
    public function setBitReadValue(int $address=0,int $bit=0,int $value=0, int $unitID = 1): array|string
    {
        if ($bit < 0 || $bit > 15) {
            return errorMessage("bit位有误");
        }
        if (!in_array($value, [0, 1])) {
            return errorMessage("bit值只有0,1");
        }
        $data_value = $this->getAddressList($address,1,$unitID);
        if (is_string($data_value)){
            return $data_value;
        }
        $old_value = $data_value[0][$address];
        if ($bit==0){
            $pos = -1;
        }else{
            $pos = "-".($bit+1);
        }
        try {
            $string = decbin($old_value);
            $string = str_pad($string, 16, "0", STR_PAD_LEFT);
            $new_value = bindec(substr_replace($string,$value,$pos,1));
            return $this->getModbusWriteMessage($address, $new_value, "uint16",$unitID);
        }catch (\Exception $exception){
            return errorMessage($exception->getMessage());
        }
    }

}
